package com.matu.service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import com.matu.domain.Adresse;
import com.matu.domain.Arret;
import com.matu.domain.Depart;
import com.matu.domain.Ligne;
import com.matu.domain.Trajet;
import com.matu.util.ToolBox;
import com.matu.util.Vitesse;


public class ServiceTisseo {
	private static String _uri = "http://pt.data.tisseo.fr/";
	private static String _key = "&key=a03561f2fd10641d96fb8188d209414d8";
	private URL url;
	private HttpURLConnection connection = null;

	// niveau de zoom pour la géolocalisation bbox
	private double zoom = 0.01;

	public ServiceTisseo() {

	}
	
	
	/**
	 * Permet d'obtenir sous forme d'un string le résultat d'une requête.
	 * @param request
	 * @return resultat
	 * @throws IOException
	 */
	public String getResultOfRequest(String request) throws IOException {

		String res = "";
		// On obtient le stream json
		String requestUri = _uri + request + _key;

		url = new URL(requestUri);
		connection = (HttpURLConnection) url.openConnection();
		InputStream is = null;
		is = connection.getInputStream();

		if (is != null) {

			// On transforme le stream en string

			BufferedReader rd = new BufferedReader(new InputStreamReader(is,
					"utf8"));
			String line;

			while ((line = rd.readLine()) != null) {
				res += line;
			}
			rd.close();
		}

		return res;
	}

	
	/**
	 * Permet de caluler le trajet pour aller à une adresse en passant par les
	 * moyens de transports de la manière la plus rapide
	 * @param adresse
	 * @return
	 * @throws IOException
	 * @throws ParseException
	 */
	public Trajet calculerLeTrajetVers(Adresse adresse) throws IOException, ParseException{
		
		ArrayList<Ligne> lignesVersDestination = getLignesVersDestination(adresse);
		ArrayList<Trajet> listeDesTrajetPossible = new ArrayList<Trajet>();
		if (lignesVersDestination!= null){
			
			for (Ligne ligne : lignesVersDestination){
				Arret possibleDepart = getServiceArretsDeLigneEnFonctionCoord(ligne.getId(),ToolBox.coordXPaulSab,ToolBox.coordYPaulSab);
				Arret possibleArriver = getServiceArretsDeLigneEnFonctionCoord(ligne.getId(),adresse.getLatitude(),adresse.getLongitude());
				int possibleTempsDeParcours = getTempsPourVoyage(ligne,possibleDepart,possibleArriver);
				listeDesTrajetPossible.add(new Trajet(ligne,possibleDepart,possibleArriver,possibleTempsDeParcours));
			}
			
			
			if (!listeDesTrajetPossible.isEmpty()){
				
				Trajet trajetSelectionner = new Trajet();
				trajetSelectionner.setTempsDeParcoursEnMinutes(99999);
				
				for (Trajet trajet : listeDesTrajetPossible){
					
					if (trajet.getTempsDeParcoursEnMinutes() < trajetSelectionner.getTempsDeParcoursEnMinutes()){
						trajetSelectionner = trajet;
					}	
				}
				return trajetSelectionner;
			}
			
		}
		
		return null;
		
	}
	
	/**
	 * Permet de calculer le temps de  parcours entre deux arrets
	 * se situant sur la même ligne.
	 * @param ligne
	 * @param depart
	 * @param arriver
	 * @return
	 * @throws IOException
	 * @throws ParseException
	 */
	public int getTempsPourVoyage(Ligne ligne,Arret depart,Arret arriver) throws IOException, ParseException{
		
		ArrayList<Arret> arrets = getServiceArretsDeLigne(ligne.getId(),true);
		
		boolean start = false;
		int count= 0;
		for (Arret a : arrets){

			if (a.getNom().equals(depart.getNom()))  start = true;
			
			if (start) count++;
			if (a.getNom().equals(arriver.getNom())) break;

		}

		// si mauvais sens
		if (count == 0){
			for (int i = arrets.size() -1 ;i >0;i--){
				Arret a  = arrets.get(i);

				if (a.getNom().equals(depart.getNom()))  start = true;
				
				if (start) count++;
				if (a.getNom().equals(arriver.getNom())) break;

			}

		}
		
		//si n'existe pas
		if (count == 0) return 9999;
		
		
		int temps = 0;
		if (ligne.getMode().equals("Metro")) temps = Vitesse.calculeVitesseMetro(count);
		else temps = Vitesse.calculeVitesseBus(count);
		
		
		return temps;
		
	}
	
	
	
	
	
	/**
	 * Permet d'obtenir les lignes communes au départ(Paul Sabatier) et à l'arrivée (adresse)
	 * @param adresse ex: jean jores
	 * @return
	 * @throws IOException
	 * @throws ParseException
	 */
	public ArrayList<Ligne> getLignesVersDestination(Adresse adresse) throws IOException, ParseException{
		
		
		 // On cherche les lignes en commun
		 ArrayList<Ligne> ligneEnCommun = new  ArrayList<Ligne>();
		 ArrayList<Ligne> ligneArriver = getServiceListeLignes(adresse.getLatitude(),adresse.getLongitude(),true);
		 ArrayList<Ligne> ligneDepart = getServiceListeLignes();
		 HashSet<String> verrfior =  new HashSet<String> ();
		 
		 for (Ligne l : ligneDepart){
			 
			 Ligne match = null;
			 for (Ligne l1 : ligneArriver){
				 
				 if (l1.getId().equals(l.getId())){
	
					 if (!verrfior.contains(l1.getId())){
						 match = new Ligne(l1.getId(),l1.getNom(),l1.getMode()) ;
						 ligneEnCommun.add(match);
						 verrfior.add(l1.getId());
					 }
					 
				 }
			 }
		 }
		 	
		 return ligneEnCommun;
	}
	
	/**
	 * Permet d'appeler le service qui permet d'obtenir les prochains 
	 * departs d'un arret choisi.
	 * 
	 * @param id indentifiant de l'arret
	 * @return la liste des depart
	 * @throws ParseException probleme avec le parsing json
	 * @throws IOException probleme avec l'obtention du service
	 */
	public ArrayList<Depart> getServiceProchainsDeparts(String id) throws IOException, ParseException{
		
		ArrayList<Depart> listeDepart = new ArrayList<Depart> ();
		// On obtien le stream json

		
		String requestUri =  "departureBoard?format=json&stopPointId=" + id ;
		String res = getResultOfRequest(requestUri);

		if(!res.equals("")){

			// on parse
			JSONParser parser = new JSONParser();
			Object obj = parser.parse(res);
			JSONObject jsonObject = (JSONObject) obj;
			
			JSONObject departures = (JSONObject) jsonObject.get("departures");
			JSONArray departure = (JSONArray) departures.get("departure");

			if (departure != null){

				@SuppressWarnings("unchecked")
				Iterator<JSONObject> iteratorDeparture= departure.iterator();
				while (iteratorDeparture.hasNext()) {
		
					JSONObject currentArea = iteratorDeparture.next();
					String dateTime = (String) currentArea.get("dateTime");
			
					if (dateTime != null){
						
						String str[] = dateTime.split(" ");
						String date = str[0];
						String heure = str[1];
						
						Depart depart = new Depart(date, heure);
						listeDepart.add(depart);
					}

				}
			}

		}

		return listeDepart;
		
		
	}
	
	
	
	/**
	 * Permet d'obtenir l'arret d'une ligne le plus proche des coordonnées
	 * @param id
	 * @param coordX
	 * @param coordY
	 * @return
	 * @throws IOException
	 * @throws ParseException
	 */
	public Arret getServiceArretsDeLigneEnFonctionCoord(String id,double coordX,double coordY) throws IOException, ParseException {

		String secteurReq = "";
		
		double x1,x2,y1,y2;
		y1 = coordY + zoom;
		x1 = coordX + zoom;
		y2 = coordY - zoom;
		x2 = coordX - zoom;
		secteurReq = "&bbox="+y1+"%2C"+x1+"%2C"+y2+"%2C"+x2+"&displayLines=1&sortByDistance=1" ;
		
		String requestUri = "stopPointsList?format=json&lineId=" + id + secteurReq ;


		String res = getResultOfRequest(requestUri);

		if(!res.equals("")){

			// on parse
			JSONParser parser = new JSONParser();
			Object obj = parser.parse(res);
			JSONObject jsonObject = (JSONObject) obj;

			JSONObject physicalStops = (JSONObject) jsonObject.get("physicalStops");
			JSONArray physicalStop = (JSONArray) physicalStops.get("physicalStop");

			if (physicalStop != null) {
				@SuppressWarnings("unchecked")
				Iterator<JSONObject> iteratorPhysicalStop = physicalStop.iterator();
				while (iteratorPhysicalStop.hasNext()) {
					JSONObject currentArea = iteratorPhysicalStop.next();
		
					String idA = (String) currentArea.get("id");
					String name = (String) currentArea.get("name");
	
					JSONArray dest = (JSONArray) currentArea.get("destinations");
					JSONObject jsonDest = (JSONObject) dest.get(0);
					String nomDest = (String) jsonDest.get("name");
			
					Arret arret = new Arret(idA, name, nomDest);
					return arret;
				}
			}

		}


		return null;

	}
	
	
	
	
	
	
	
	
	
	/**
	 * Permet d'appeler le service qui permet d'obtenir les arrets d'une ligne
	 * dans un secteur donné.
	 * @param id indentifiant de la ligne
	 * @param secteur secteur dans lequel doit se porter la recherche
	 * @return la liste des arret
	 * @throws ParseException probleme avec le parsing json
	 * @throws IOException probleme avec l'obtention du service
	 */
	public ArrayList<Arret> getServiceArretsDeLigne(String id) throws IOException, ParseException{
		return getServiceArretsDeLigne(id,false);
	}
	
	public ArrayList<Arret> getServiceArretsDeLigne(String id,boolean noDouble)
			throws IOException, ParseException {

		HashSet<String> checkNoDouble = new HashSet<String> ();
		ArrayList<Arret> listeArret = new ArrayList<Arret> ();
		
		
		String requestUri =  "stopPointsList?format=json"
				+ "&displayDestinations=1&lineId=" + id;


		String res = getResultOfRequest(requestUri);

		if(!res.equals("")){

			// on parse
			JSONParser parser = new JSONParser();
			Object obj = parser.parse(res);
			JSONObject jsonObject = (JSONObject) obj;

			JSONObject physicalStops = (JSONObject) jsonObject.get("physicalStops");
			JSONArray physicalStop = (JSONArray) physicalStops.get("physicalStop");

			if (physicalStop != null) {
				@SuppressWarnings("unchecked")
				Iterator<JSONObject> iteratorPhysicalStop = physicalStop.iterator();
				while (iteratorPhysicalStop.hasNext()) {
					JSONObject currentArea = iteratorPhysicalStop.next();
		
					String idA = (String) currentArea.get("id");
					String name = (String) currentArea.get("name");
					//System.out.println("-- " + idA + " -> " + name);
	
					JSONArray dest = (JSONArray) currentArea.get("destinations");
					JSONObject jsonDest = (JSONObject) dest.get(0);
					String nomDest = (String) jsonDest.get("name");
					Arret arret = new Arret(idA, name, nomDest);
					
					if (noDouble){

						if (!checkNoDouble.contains(arret.getNom())){
							listeArret.add(arret);
							checkNoDouble.add(arret.getNom());
						}
					}
					else{
						listeArret.add(arret);
					}
				}
			}

		}

		return listeArret;

	}
	
	
	
	
	
	/**
	 * Permet d'appeler le service qui fourni la liste des lignes.
	 * 
	 * @return Un ArrayList de Ligne
	 * @throws ParseException probleme avec le parsing json
	 * @throws IOException probleme avec l'obtention du service
	 */
	public ArrayList<Ligne> getServiceListeLignes() throws ParseException,
	IOException {
		
		return getServiceListeLignes(ToolBox.coordXPaulSab,ToolBox.coordYPaulSab,false);
	}
	
	
	public ArrayList<Ligne> getServiceListeLignes(double coordX,double coordY,boolean limited) throws ParseException,
			IOException {
		
		ArrayList<Ligne> listeLigne = new ArrayList<Ligne> ();
		HashSet<String> verrfior =  new HashSet<String> ();
		String limitedS = "";
		if (limited) limitedS = "&sortByDistance=1";
		
		
		String secteurReq = "";
		
		double x1,x2,y1,y2;
		y1 = coordY + zoom;
		x1 = coordX + zoom;
		y2 = coordY - zoom;
		x2 = coordX - zoom;
		secteurReq = "&bbox="+y1+"%2C"+x1+"%2C"+y2+"%2C"+x2+"&displayLines=1" ;
	
		
		String requestUri = "stopPointsList?format=json" + secteurReq + limitedS ;
		String res = getResultOfRequest(requestUri);

		if(!res.equals("")){

			// on parse
			JSONParser parser = new JSONParser();
			Object obj = parser.parse(res);
			JSONObject jsonObject = (JSONObject) obj;

			JSONObject physicalStops = (JSONObject) jsonObject.get("physicalStops");

			JSONArray physicalStop = (JSONArray) physicalStops.get("physicalStop");

			if (physicalStop != null){
				@SuppressWarnings("unchecked")
				Iterator<JSONObject> iteratorPhysicalStop = physicalStop.iterator();
				while (iteratorPhysicalStop.hasNext()) {
					JSONObject currentArea = iteratorPhysicalStop.next();
			
		
					JSONArray destinations = (JSONArray) currentArea.get("destinations");
					
					
					if (destinations != null){
						@SuppressWarnings("unchecked")
						Iterator<JSONObject> iteratordestinations = destinations.iterator();
						
						while (iteratordestinations.hasNext()) {
							
							JSONObject currentDestination = iteratordestinations.next();
							
							JSONArray lines = (JSONArray) currentDestination.get("line");
							if (lines != null){
			
								@SuppressWarnings("unchecked")
								Iterator<JSONObject> iteratorlines = lines.iterator();
					
								
								while (iteratorlines.hasNext()) {
									JSONObject currentLine = iteratorlines.next();
					
									String id = (String) currentLine.get("id");
									String name = (String) currentLine.get("name");
									String type = (String) currentLine.get("shortName");
									
									if (type.equals("A") || type.equals("B") || type.equals("T1")) type = "Metro";
									else type = "Bus"; 
									
									if (!verrfior.contains(id)){
										
										Ligne nLine = new Ligne(id,name,type);
										listeLigne.add(nLine);
										verrfior.add(id);
									}
								}
							}
					
						}
					}
				}
			}

		}

		return listeLigne;

	}
}
